#pragma once
#include "log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"
#include <memory>

namespace SocketModule
{
    //模板方法模式
    using namespace LogModule;
    class Socket;

    using SockPtr = std::shared_ptr<Socket>;
    class Socket
    {
    public:
        virtual ~Socket() = default;
        virtual void SockOrDie() = 0;
        virtual void SetSocketOpt() = 0;
        virtual bool BindOrDie(int port) = 0;
        virtual bool ListenOrDie() = 0;
        virtual SockPtr Accept(InetAddr *client) = 0;
        virtual void Close() = 0;
        virtual int Recv(std::string& in) = 0;
        virtual int Send(std::string& out) = 0;
        virtual int Fd() = 0;
        //提供一个创建listensockfd的固定讨论
        void BuildTcpSocketMethod(int port)
        {
            SockOrDie();
            SetSocketOpt();
            BindOrDie(port);
            ListenOrDie();
        }
    };
    class TcpSocket : public Socket
    {
    public:
        TcpSocket() :_sockfd(gdefaultsockfd)
        {}
        TcpSocket(int sockfd):_sockfd(sockfd)
        {}
        virtual ~TcpSocket()
        {
            //::close(_sockfd);
        }
        virtual void SockOrDie() override
        {
            _sockfd = ::socket(AF_INET,SOCK_STREAM,0);
            if(_sockfd < 0)
            {
                LOG(ERROR)<<"socket error";
                exit(SOCKET_ERR);
            }
            LOG(INFO) << "socket success: "<<_sockfd;
        }
        virtual void SetSocketOpt() override
        {}
        virtual bool BindOrDie(int port) override
        {
            if(_sockfd == gdefaultsockfd)
                return false;
            InetAddr addr(port);
            int n = ::bind(_sockfd,addr.NetAddr(),addr.NetAddrLen());
            if(n<0)
            {
                LOG(ERROR)<<"bind error";
                exit(BIND_ERR);
            }
            LOG(INFO)<<"bind success: "<<_sockfd;
            return true;
        }
        virtual bool ListenOrDie() override
        {
            if(_sockfd == gdefaultsockfd)
                return false;
            int n = ::listen(_sockfd,gbacklog);
            if(n<0)
            {
                LOG(ERROR)<<"listen error";
                exit(LISTEN_ERR);
            }
            LOG(INFO)<<"listen success: "<<_sockfd;
            return true;
        }
        virtual SockPtr Accept(InetAddr *client) override
        {
            if(!client)
                return nullptr;
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            int sockfd = ::accept(_sockfd,CONV(&peer),&len);
            if(sockfd < 0)
            {
                LOG(WARNING)<<"accept error";
                return nullptr;
            }
            client->SetAddr(peer,len);
            return std::make_shared<TcpSocket>(sockfd);
        }
        virtual void Close() override
        {
            if(_sockfd == gdefaultsockfd)
                return;
            ::close(_sockfd);
        }
        virtual int Send(std::string& out) override
        {
            int n = ::send(_sockfd,out.c_str(),out.size(),0);
            return n;
        }
        virtual int Recv(std::string& in) override
        {
            char buffer[1024*8];
            int n = -1;
            n = ::recv(_sockfd,buffer,sizeof(buffer)-1,0);
            if(n >0)
            {
                buffer[n] = 0;
                in = buffer;
            }
            return n;
        }
        virtual int Fd() override
        {
            return _sockfd;
        }
    private:
        int _sockfd;
    };
}